/*
 * Created on 2006-6-8
 *
 * TODO To change the template for this generated file go to
 * Window - Preferences - Java - Code Style - Code Templates
 */
package com.powerunion.datacollection.report.excelreport;

import java.io.OutputStream;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import com.powerunion.datacollection.report.excelreport.base.IReportWriter;
import com.powerunion.datacollection.report.excelreport.base.Parameter;
import com.powerunion.datacollection.report.excelreport.base.element.ElementFactory;
import com.powerunion.datacollection.report.excelreport.base.element.FieldVariable;
import com.powerunion.datacollection.report.excelreport.base.element.ParameterVariable;
import com.powerunion.datacollection.report.excelreport.base.element.Variable;
import com.powerunion.datacollection.report.excelreport.config.DataSourceConfig;
import com.powerunion.datacollection.report.excelreport.config.ReportConfig;
import com.powerunion.datacollection.report.excelreport.config.ReportConfig.SheetConfig;
import com.powerunion.datacollection.report.excelreport.datasource.BaseDataSource;
import com.powerunion.datacollection.report.excelreport.datasource.IDataSource;

import jxl.Workbook;
import jxl.WorkbookSettings;
import jxl.write.WritableSheet;
import jxl.write.WritableWorkbook;

/**
 * 报表生成核心类，该类负责每个具体报表的初始化(如从配置管理对象reportManager中取得具体报表对应的
 * 配置信息，并根据报表的配置信息生成相应风格报表的生成器对象(如CardStyleReport)，
 * 在完成对生器对象的初始化后调用后者去分析模板文件并生成相应的报表数据。<br>
 * 注：每一个报表输出请求都会对应一个报表(report)对象实例，
 * @author juny
 */
public class Report {
    private static Logger log = Logger.getLogger(Report.class);
    /**
     * 构造一个报表对象，并传入对象实例的构建者(报表引擎对象)，因为一些配置参数信息和配置对象都只有在报表引擎对象中可见。 
     * @param rm 报表引擎对象实例
     */
    public Report(ReportEngine rm){
        this.reportEngine = rm;
    }
    
    /**
     * 初始化报表，初始化配置文件，报表数据源等信息。
     * @param config	当前需打印报表的配置信息对象
     * @param params	参数map
     * @throws Exception
     */
    public void initializeReport(ReportConfig config, Map params) 
    										throws Exception{
        this.reportConfig = config;
        this.params = params;
        
        //取得数据源
        if(log.isDebugEnable()){
            log.debug("Getting data source.....");
        }
	    this.dataSources = getAllDataSource(reportConfig, params);
	    if(null == this.dataSources){
	        throw new Exception("System couldn't get any datasource!");
	    }
	    
	    //初始化所有数据源的初始化参数配置信息<param-init name="arg1" value="$F{ds.field}"/>
	    initAllDataSourceParams(params);
	    
	    //运行用户嵌入的对象
	    invokeEmbeddedObjects(reportConfig, this.dataSources, params);
    }
    
    /**
     * 释放报表生成过程中用到的一些资源
     */
    public void release(){
        if(null != this.dataSources){
            try{
	            Collection values = this.dataSources.values();
	            Iterator itr = values.iterator();
	            while(itr.hasNext()){
	                ((IDataSource)itr.next()).destroy();
	            }
            }catch(Exception e){
                log.error("While release report." + e.getMessage(), e);
            }
        }
    }
    
    /**
     * 根据传入参数,取得所有报表数据源对象实例，并初始化好数据源.
     * @param report	当前需打印报表的配置信息对象
     * @param params	参数map
     * @return			返回初始化好的数据源实例map
     * @throws Exception
     */
    private Map getAllDataSource(ReportConfig report, Map params)
    	throws Exception {
        Map dataSource = new HashMap();
        SheetConfig sheet = null;
        DataSourceConfig dsConfig = null;
        BaseDataSource ds = null;
        String[] sheets = report.getSheetName();
        Object[] dsNames = null;
        
        for(int i=0; i<sheets.length; i++){
            sheet = report.getSheet(sheets[i]);
            dsNames = sheet.getDataSourceNames();
            for(int j=0; j<dsNames.length; j++){
                String dsName = (String)dsNames[j];
                dsConfig = sheet.getDataSource(dsName);
                ds = (BaseDataSource)reportEngine.getDataSourceInstance(dsConfig, dsName);
                //取得该数据源实例在配置文件中配置的初始化参数
                ds.setParamInit(sheet.getDataSourceParamInit(dsName));
                if(getParamsValue(ds, params)){
                    //初始化数据源时暂时不检索数据。
                    //ds.queryData();
                }
                dataSource.put((String)dsNames[j], ds);
            }
        }
        
        return dataSource;
    }
    
    /**
     * 初始化所有输入数据源的输入参数
     * @param params	请求打印报表的输入参数map
     */
    private void initAllDataSourceParams(Map params){
        Iterator itr = this.dataSources.entrySet().iterator();
        Entry entry = null;
        BaseDataSource ds = null;
        while(itr.hasNext()){
            entry = (Entry)itr.next();
            ds = (BaseDataSource)entry.getValue();
            initDataSourceParam(ds, params);
        }
    }
    
    /**
     * 初始化输入数据源的输入参数。该函数会根据数据源配置的param-init节点的参数配置信息，
     * 生成相应的变量对象，如(配置一个$F{dsName.field1})会生成一个对应dsName.fields的变量对象实例)，
     * 配置一个$P{param1}则又会生成一个对应param1的参数变量对象实例。如果两者都不是则系统会生成一个
     * 常量对象实例。
     * @param ds	数据源对象实例
     * @param params	请求打印报表的输入参数map
     */
    private void initDataSourceParam(BaseDataSource ds, Map params){
        Map paramInit = ds.getParamInit();
        List dsParams = null;
        Variable var = null;
        
        if(null != paramInit){
            dsParams = ds.getConfig().getParams();
            List paramTypes = ds.getConfig().getParamTypes();
            
            if(null != dsParams){
                if(dsParams.size() != paramInit.size()){
                    log.warn("The initialial parameters are not equal the datasource config parameters!");
                }
                
                for(int i=0; i<dsParams.size(); i++){
                    if(log.isDebugEnable()){
	                    log.debug("param-init name=" + dsParams.get(i) + 
	                            "  value=" + paramInit.get(dsParams.get(i))+
	                            " type=" + paramTypes.get(i));
                    }
                    
                    var = ElementFactory.getVariableInstances(
                            	paramInit.get(
                            	    (String)dsParams.get(i)
                            	).toString()
                            );
                    
                    if(null != var){
                        //设置变量的数据类型
                        var.setValueType((String)paramTypes.get(i));
                        //配置相应变量对象的初始化参数。
                        if(var.getType() == Variable.VARIABLE_TYPE_FIELD){
                            ((FieldVariable)var).setDataSource(this.dataSources);
                        }else if(var.getType() == Variable.VARIABLE_TYPE_PARAMETER){
                            ((ParameterVariable)var).setParam(params);
                        }
                        //将配置好的变量对象替换原来的变量定义字符串
                        paramInit.put((String)dsParams.get(i), var);
                    }
                }
            }
        }
    }
    
    /**
     * 查询报表是否存在嵌入对象，如果存在则生成该对象并调用该对象处理函数。
     * @param reportConfig	当前打印报表对应的配置对象实例
     * @param dataSources	当前打印报表对应的所有数据源
     * @param params		打印报表请求输入的参数map
     * @throws Exception
     */
    private void invokeEmbeddedObjects(ReportConfig reportConfig,
            Map dataSources, 
            Map params) throws Exception {
        String objectName = reportConfig.getEmbeddedObject();
        if(null == objectName || "".equals(objectName)){
            return;
        }
        
        try{
            if(log.isDebugEnable()){
                log.debug("Load embedded object for class " + objectName);
            }
            ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
            if (classLoader == null) {
                classLoader = ReportEngine.class.getClassLoader();
            }
            
            Object obj = classLoader.loadClass(objectName).newInstance();
            if(obj instanceof IReportEntry){
                IReportEntry reportEntry = (IReportEntry)obj;
                reportEntry.execute(reportConfig, dataSources, params);
            }else{
                log.error("Embedded objects must be instance of IReportEntry");
            }
        }catch(Exception e){
            log.error("Catched an Exception when process user embedded object " + objectName, e);
            throw e;
        }
    }
    
    /**
     * 将报表数据写到传入的输出流中。
     * @param os
     * @throws Exception
     */
    public void writeData(OutputStream os) throws Exception {
        WritableWorkbook wb = null;
        try {
            if(log.isDebugEnable()){
                log.debug("Begin create workbook.");
            }
            
            Workbook wbTemplet = this.reportConfig.getTempletWorkbook();
            WorkbookSettings ws = new WorkbookSettings();
            ws.setInitialFileSize(this.reportConfig.getInitialFileSize());
            ws.setArrayGrowSize(this.reportConfig.getGrowSize());
            wb = Workbook.createWorkbook(os, wbTemplet, ws);
            
            if(log.isDebugEnable()){
                log.debug("create workbook success! [" + ws.getInitialFileSize() + "]");
            }
            
            String[] sheetNames = reportConfig.getSheetName();
            WritableSheet sheet = null;
            
            if(log.isDebugEnable()){
                log.debug("Writing data...");
            }
            for (int i = 0; i < sheetNames.length; i++) {
                sheet = wb.getSheet(sheetNames[i]);
                if (null == sheet) {
                    log.error("Can't get the sheet with the specified name(:"
                                    + sheetNames[i] + ") from templet file");
                } else {
                    writeSheet(sheet, reportConfig.getSheet(sheetNames[i]));
                } 
            }

            wb.write();
            if(log.isDebugEnable()){
                log.debug("Writing data success!");
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw e;
        } finally {
            if (wb != null)
                wb.close();
        }
    }    
    
    /**
     * 按照当前excel sheet模板定义的格式生成相应的excel报表。<br>
     * 该函数首先根据sheet配置对象生成对应报表生成对象实例。<br>
     * 在调用后者来生成具体的报表。
     * @param sheet		用户定义的模板sheet对象
     * @param sheetConfig	当前模板sheet对应的配置对象。
     * @throws Exception
     */
    private void writeSheet(WritableSheet sheet, 
            SheetConfig sheetConfig
        ) throws Exception{
        //根据报表的配置文件取得数据读写对象
        IReportWriter writer = WriterFactory.getWriterFactory()
        							.getReportWriter(sheetConfig);
        if(null == writer){
            throw new Exception("System couldn't get report writer! " +
            		"Please confirm you have defined a correct report style!");
        }
        writer.setReportConfig(sheetConfig);
        writer.setDataSources(dataSources);
        writer.setParameters(params);
        writer.analysisTemplet(sheet);
        
        Parameter param = new Parameter();
        param.dataSources = dataSources;
        param.sheet = sheet;
        param.params = params;
        writer.writeData(param);
    }
    
    /*
     * 取得参数值
     */
    private boolean getParamsValue(IDataSource ds, Map params){
        String key = null, value = null;
        boolean bRet = true;
        
        DataSourceConfig dsConfig = ds.getConfig();
        String dsName = ds.getName();
        List cfgParams = dsConfig.getParams();
        for (int i = 0; i < cfgParams.size(); i++) {
            key = dsName + "." + cfgParams.get(i);
            value = (String) params.get(key);
            if (null == value || "".equals(value)) {
                log.warn("Fail to get the parameter's value. " +
                		"datasource name=" + dsName +
                		"parameter name=" + cfgParams.get(i));
                bRet = false; //没有配制完所有的参数。
            } else {
                ds.setParameter((String) cfgParams.get(i), value);
            }
        }
        
        return bRet;
    }
    private ReportConfig reportConfig = null;
    private Map dataSources = null; //保存所有数据源信息.
    private ReportEngine reportEngine = null;
    private Map params = null;

}
